package com.zeyuan.rpcclient.test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class ServerSelector {
    public static void main(String[] args) throws IOException {
        //创建selector
        Selector selector=Selector.open();
        ByteBuffer buffer=ByteBuffer.allocate(10);
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        //建立selector和channel的联系
          //SelectionKey将来事件发生后，通过他可以知道什么事件以及哪个channel发生的,相当于管理ssc的管理员
                    //事件：accept有连接请求时触发，connect客户端那边连接建立后触发的事件,read数据到达并且可读,write可写事件
        SelectionKey register = ssc.register(selector, 0, null);
        /*原理：selector容器中放入register这个key，格式为key@hash,关注(指向)ssc，监听其accept事件，
        * select()方法发现新事件的时候会创建一个新集合（selectedKeys   Set<SelectionKey> selectionKeys = selector.selectedKeys();），将上面的key复制到这个新集合中
        * 新集合只会添加不会删除，如果accept并处理这个事件，就会在新集合中删除这个key里面的hash，key还在。
        *
        * register方法就是往selector里放key@hash监听对应channel事件
        * select()方法创建新集合，如果select监听到事件，则往新集合中放入key@hash，否则阻塞
        * accept()方法用于建立连接，没有连接建立或者已经建立过则返回null，由于新集合只会添加不会删除，如果事件在上一次已经处理过，新集合中并未删除，必须要手动remove，否则accept返回null
        *
        * */
        System.out.println("register key{}"+register);
        //这个selectionkey只关心accept事件(accept,connect,read,write事件)
        register.interestOps(SelectionKey.OP_ACCEPT);
        ssc.bind(new InetSocketAddress(8888));
        while (true){
            System.out.println("开始监听");
            //select方法
            selector.select(); //在没有事件的时候阻塞，有事件的时候恢复运行
            //处理事件 selectedKeys内部包含所有发生的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            //如果这里不对连接事件进行处理，那上面select()会认为是同一个事件，不进行阻塞，就会不停执行while(true)
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();//通过这个key可以知道发生了什么事件以及是哪个channel发生的
                iterator.remove(); //新集合只会添加不会删除，如果不从selectedKeys集合中删除，下次重新处理这个已处理的事件accept返回null
                if (key.isAcceptable()){ //根据不同事件类型做出不同操作 如果是accept事件
                    try {
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        //如果不想处理事件，就把accept换成cancel，这样即使不处理事件也会在select()上阻塞
                        SocketChannel accept = channel.accept();
                        accept.configureBlocking(false);
                        //socketchannel也注册到selector上，可以通过register1拿到事件
                        SelectionKey register1 = accept.register(selector, 0, null);
                        register1.interestOps(SelectionKey.OP_READ);
                        int read = accept.read(buffer);
                        if (read==-1){ //如果客户端正常断开,read为-1
                            key.cancel();
                        }

                        System.out.println("有连接请求");
                    }catch (IOException e){
                        key.cancel(); //有其中一个客户端异常断开连接(抛出异常)如果不从selector集(老集合)中删除key，否则会不停while
                        //但如果客户端sc.close主动关闭,不会抛出异常,channel在read的时候判定是否返回-1即可
                    }

                }else if (key.isReadable()){ //如果是read事件
                    SocketChannel channel = (SocketChannel) key.channel();
                    ByteBuffer bf=ByteBuffer.allocate(10);
                    channel.read(bf);
                    buffer.flip();
                    System.out.println("有读取请求");
                }


                System.out.println("成功");
            }

        }

    }
}
